import { TelegramLink } from "@telegram-bot/common";

import { Chat } from "../database/entities/chat.entity.js";
import { Message } from "../database/entities/message.entity.js";
import { User } from "../database/entities/user.entity.js";
import { imageHash } from "../libs/index.js";

/**
 * @typedef {import("grammy/out/context").Context} Context
 */

export class MessageMedia {
  /** @type {import("../types").IConfig} */
  #config;

  /** @param {import("../types").IConfig} config */
  constructor(config) {
    this.#config = config;
  }

  /** @param {import("grammy/out/bot").Bot<import("@grammyjs/files").FileFlavor<Context>>} bot */
  handle(bot) {
    bot.on("message:media", async (ctx) => await this.#check(ctx));
  }

  /** @param {import("@grammyjs/files").FileFlavor<Context>} ctx */
  async #check(ctx) {
    if (!ctx.message) return;

    const chat = await this.#getChat(ctx.message);
    const user = await this.#getUser(ctx.message);

    const messages = await this.#tmp(ctx, chat, user);
    if (!messages?.length) return;

    await this.#reply(ctx, messages, chat);
  }

  /**
   * @description
   *
   *
   * @param {import("@grammyjs/files").FileFlavor<Context>} ctx
   * @param {Message[]} messages
   * @param {Chat} chat
   * @returns {Promise<string>}
   */
  async #reply(ctx, messages, chat) {
    const { message_id } = ctx.message;
    const text = await this.#makeReplyText(messages, chat);
    await ctx.reply(text, {
      reply_parameters: { message_id },
      parse_mode: "MarkdownV2",
      link_preview_options: { is_disabled: true },
    });
  }

  /**
   * @description
   *
   *
   * @param {Message[]} messages
   * @param {Chat} chat
   * @returns {Promise<string>}
   */
  async #makeReplyText(messages, chat) {
    const header = "Found a duplicate image\n\n";

    const footer = messages
      .map((msg) => {
        const messagelink = `[${msg.messageId}](${TelegramLink.message(chat.parsedChatId, msg.messageId)})`;
        const authorlink = `[@${msg.user.username ?? "unknown"}](${TelegramLink.user(msg.user.username)})`;

        return `Message: ${messagelink} Author: ${authorlink}\n`;
      })
      .join("");

    return header + footer;
  }

  /** @param {import("@grammyjs/types").Message} message  */
  async #getChat(message) {
    const { chat } = message;
    return await Chat.findOneAndUpdate(
      { chatId: chat.id },
      { chatId: chat.id, title: chat.title, type: chat.type },
      {
        new: true,
        upsert: true,
      },
    );
  }

  /** @param {import("@grammyjs/types").Message} message  */
  async #getUser(message) {
    const { from } = message;
    return await User.findOneAndUpdate(
      { userId: from.id },
      {
        userId: from.id,
        firstName: from.first_name,
        lastName: from.last_name,
        username: from.username,
        languageCode: from.language_code,
        isBot: from.is_bot,
        isPremium: from.is_premium,
      },
      {
        new: true,
        upsert: true,
      },
    );
  }

  /**
   * @description
   *
   *
   * @param {import("@grammyjs/files").FileFlavor<Context>} ctx
   * @param {Chat} chat
   * @param {User} user
   * @returns {Promise<string>}
   */
  async #tmp(ctx, chat, user) {
    const file = await ctx.getFile();

    const fileBuffer = await fetch(file.getUrl()).then(async (res) => Buffer.from(await res.arrayBuffer()));

    const hash = await imageHash(fileBuffer);

    const messages = await Message.find({ "media.hash": hash, chatId: chat._id }, undefined, { populate: "user" });

    await Message.create({
      chatId: chat._id,
      userId: user._id,
      messageId: ctx.message.message_id,
      date: ctx.message.date,
      mediaGroupId: ctx.message.media_group_id,
      media: { id: file.file_id, uniqueId: file.file_unique_id, size: file.file_size, hash },
    });

    return messages;
  }
}
